import tkinter as tk
from tkinter import messagebox
import re
import random

# 正则词典(元字符：说明的关键词)
PREG = {
    '\\d': ['数字', '数值'],
    '\\w': ['单词', '文字', '字符', '非空白字符'],
    '岁': ['年龄', '岁数'],
    '\\s': ['空白字符']
}

# 提示词关系到适应度函数，所以非常重要
keywords = []
# 初始化遗传算法
data = []  # 种群基因数据库
size = 10  # 种群规模
# 大型项目中可以有多个输入和输出，只不过那就变成了机器学习
input_str = ''  # 输入字符串
output_str = ''  # 最终字符串（最终基因序列）
testput = ''  # 测试字符串
sex = 0.8  # 交叉率
ag = 0.1  # 变异率
max_iter = 100  # 最大迭代次数，防止死循环

# 初始化种群
def start():
    global keywords, input_str, output_str, testput, data
    # 获取需要测试的数据
    keywords = word_entry.get().split(' ')
    input_str = input_entry.get()
    output_str = output_entry.get()
    testput = test_entry.get()
    data = []
    for i in range(size):
        gene = []  # 新基因数组
        # 基因的生成很关键，最好按照正则语法来生成
        for key in PREG:
            arr = PREG[key]
            weight = 0.1  # 基本权重
            for item in keywords:
                # 如果命中随机增加权重
                if item in arr:
                    weight += random.random()
            if weight > random.random():
                # 添加基因片段并设置默认数量（默认至少1个）
                gene.append([key, '{1,}'])
        # 追加到种群中
        if len(gene) > 0:
            data.append(gene)
            print('新个体：', gene)
    print('初始化种群', data)

# 适应度函数：这里可以使用简单的关键词命中率来判断与提示词的相似度
def ok(arr):
    words = []
    for i in range(len(arr)):
        name = arr[i][0]
        words.extend(PREG[name])
    len_count = 0
    # 判断当前基因片段是否在最终基因序列中，与顺序无关
    for item in keywords:
        if item in words:
            len_count += 1
    count = len_count / len(keywords) if len_count > 0 else 0
    return count

# 遗传算法
def heredity():
    global max_iter, data
    if len(data) < 2:
        print('由于种群少于2个人，重新初始化种群！')
        start()
    # 适应值排序
    data.sort(key=lambda x: ok(x), reverse=True)
    # 显示本次排序的数据
    print('排序后的种群', data)
    # 判断是否找到基因序列
    if run(data[0]):
        result_label.config(text='已经找到基因：' + str(data[0]))
        return data[0]
    elif max_iter < 1:
        result_label.config(text='目前最优秀的基因是：' + str(data[0]))
        return data[0]
    else:
        max_iter -= 1
        print('还剩', max_iter, '进化次机会')
    # 参与遗传的个体
    is_l = len(data)
    sex_i = round(sex * is_l)
    sexs = []
    # 轮盘选择方法
    sum_val = 0
    arr = []
    for n in range(is_l, 0, -1):
        sum_val += n
        arr.append(sum_val)
    for n in range(sex_i):
        rand = random.random() * sum_val
        for a in range(len(arr)):
            if rand < arr[a]:
                sexs.append(data[a])  # 被选中
                break
    print(sexs)
    # 生成新的种群
    news = []
    for i in range(size):
        new_x = random.randint(0, len(sexs) - 1)
        new_y = random.randint(0, len(sexs) - 1)
        xy = arr_replace(sexs[new_x], sexs[new_y]) if random.random() > 0.5 else arr_replace(sexs[new_y], sexs[new_x])
        news.append(xy)
    data = news
    # 递归
    root.after(1000, heredity)

# 基因片段替换函数
def arr_replace(a1, a2):
    new_arr = []  # 新基因
    for key in range(len(a1)):
        if random.random() < ag:
            # 是否增加新的基因
            for preg in PREG:
                arr = PREG[preg]
                weight = 0.1  # 基本权重
                for item in keywords:
                    if item in arr:
                        weight += random.random()
                if weight > 0.5:
                    # 添加新的基因
                    new_arr.append([preg, '{1,}'])
        else:
            # 随机交换
            if random.random() > 0.5:
                new_arr.append(a1[key])
            else:
                if key < len(a2):
                    # 如果对方存在该基因序列就替换
                    new_arr.append(a2[key])
                else:
                    new_arr.append(a1[key])
    return new_arr

# 运行正则代码
def run(arr):
    str_val = ''
    for item in arr:
        str_val += item[0] + item[1]
    print(str_val)
    # 运行这段代码
    try:
        preg = re.compile(str_val)
        # 返回匹配结果
        result = preg.search(testput)
        data = preg.findall(testput)
        print('测试结果', data)
        return result
    except Exception as e:
        # 错误提示
        print(e)
    return False

# 创建主窗口
root = tk.Tk()
root.title("进化编程")

# 创建标签和输入框
tk.Label(root, text="关键提示词").pack()
word_entry = tk.Entry(root)
word_entry.insert(0, "数字 年龄")
word_entry.pack()

tk.Label(root, text="输入字符串").pack()
input_entry = tk.Entry(root)
input_entry.insert(0, "我今年10岁了")
input_entry.pack()

tk.Label(root, text="输出字符串").pack()
output_entry = tk.Entry(root)
output_entry.insert(0, "10")
output_entry.pack()

tk.Label(root, text="测试字符串").pack()
test_entry = tk.Entry(root)
test_entry.insert(0, "小明10岁了")
test_entry.pack()

# 创建按钮
button = tk.Button(root, text="运行", command=heredity)
button.pack()

# 显示结果的标签
result_label = tk.Label(root, text="")
result_label.pack()

# 启动主循环
root.mainloop()